/*
 * ===========================================================================
 *
 *      Filename: darray.c
 *   Description: dynamic array
 *        Author: Ye wenhao, wenhaoye@126.com
 *  Organization: 
 *
 * ===========================================================================
 */
#include "darray.h"
#include "def.h"

#define DARRAY_DEFAULT_SIZE    64

struct darray
{
    uint32_t nums;
    uint32_t size;
    locker_t *locker;
    void **arrays;
};

darray_t *darray_create(uint32_t init_size, locker_t *locker)
{
    darray_t *self = (darray_t *)MALLOC(sizeof(darray_t));
    if(self) {
        self->locker = locker;
        self->nums = 0;
        if(init_size > DARRAY_DEFAULT_SIZE)
            self->size = init_size;
        else
            self->size = DARRAY_DEFAULT_SIZE;

        self->arrays = (void **)MALLOC(sizeof(void *) * (self->size));
        if(self->arrays == NULL)
            safe_free(self);
    }

    return self;
}

void darray_destroy(darray_t **p_self, darray_item_destroy_func destroy)
{
    if(p_self && *p_self){
        darray_t *self = *p_self;
        if(destroy){
            uint32_t i;
            locker_lock(self->locker);
            for(i = 0; i < self->size; ++i){
                if(self->arrays[i]){
                    (*destroy)(self->arrays[i]);
                    self->nums--;
                }
            }
            locker_unlock(self->locker);
        }

        locker_destroy(self->locker);
        safe_free(self->arrays);
        safe_free(self);
    }
}

static int _darray_expand(darray_t *self)
{
    if(self->nums >= self->size){
        uint32_t size = self->size + ((self->size) >> 1);
        void **p = (void **)realloc(self->arrays, sizeof(void *) * size);
        if(p == NULL){
            ERR("realloc failed");
            return -1;
        }

        self->arrays = p;
        self->size = size;
    }

    return 0;
}

static int _darray_insert(darray_t *self, uint32_t index, void *data)
{
    if(index > self->nums)
        index = self->nums;

    if(_darray_expand(self) < 0){
        ERR("_darray_expand failed");
        return -1;
    }   

    if(self->nums > 0){
        uint32_t i;
        for(i = self->nums-1; i >= index; --i){
            self->arrays[i+1] = self->arrays[i];
            if(i == 0)
                break;
        }
    }

    self->arrays[index] = data;
    self->nums++;
    return 0;
}

int darray_insert(darray_t *self, uint32_t index, void *data)
{
    if(self == NULL)
        return -1;

    locker_lock(self->locker);
    int ret = _darray_insert(self, index, data);
    locker_unlock(self->locker);
    return ret;
}

int darray_insert_tail(darray_t *self, void *data)
{
    if(self){
        locker_lock(self->locker);
        int ret = _darray_insert(self, self->nums, data);
        locker_unlock(self->locker);
        return ret;
    }

    return -1;
}

static int _darray_drain(darray_t *self)
{
    if(self->size > DARRAY_DEFAULT_SIZE){
        uint32_t tmp_size = (self->nums) >> 2;
        if(self->nums <= tmp_size){
            tmp_size = (self->nums) >> 1;
            if(tmp_size < DARRAY_DEFAULT_SIZE)
                tmp_size = DARRAY_DEFAULT_SIZE;
            
            void *_p = realloc(self->arrays, sizeof(void *) * tmp_size);
            _p = _p; // for clean compiler warning
            self->size = tmp_size;
        }
    }

    return 0;
}

static int _darray_remove(darray_t *self, uint32_t index, void **data)
{
    if(index >= self->nums)
        return -1;

    if(data){
        *data = self->arrays[index];
    }

    uint32_t i;
    for(i = index; i < self->nums; ++i){
        self->arrays[i] = self->arrays[i+1];
    }
    self->nums--;

    _darray_drain(self);
    return 0;
}

int darray_remove(darray_t *self, uint32_t index, void **data)
{
    if(self == NULL)
        return -1;

    locker_lock(self->locker);
    int ret = _darray_remove(self, index, data);
    locker_unlock(self->locker);
    return ret;
}

int darray_remove_tail(darray_t *self, void **data)
{
    if(self == NULL)
        return -1;

    locker_lock(self->locker);
    int ret = _darray_remove(self, self->nums-1, data);
    locker_unlock(self->locker);
    return ret;
}

static void *_darray_get(darray_t *self, uint32_t index)
{
    if(index >= self->nums)
        return NULL;

    return self->arrays[index];
}

void *darray_get(darray_t *self, uint32_t index)
{
    if(self == NULL)
        return NULL;

    locker_lock(self->locker);
    void *data = _darray_get(self, index);
    locker_unlock(self->locker);
    return data;
}

void *darray_get_tail(darray_t *self)
{
    if(self == NULL)
        return NULL;

    locker_lock(self->locker);
    void *data = _darray_get(self, darray_nums(self)-1);
    locker_unlock(self->locker);
    return data;
}

uint32_t darray_nums(darray_t *self)
{
    if(self)
        return self->nums;
    return 0;
}
